/*
 * commons is a based project implemented
 * Copyright (C) 2024 Jasmine
 */
package com.commons.core.config;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;

import com.commons.core.constant.PropertiesConstants;
import com.commons.core.utils.ExceptionUtil;
import com.commons.core.utils.ExecuteUtils;

/**
 * 密码配置类,默认编解码器使用的是BCryptPasswordEncoder 编码后的密码是遵循一定规则的{idForEncode}encodePassword,前缀{}包含了编码的方式再拼接上该方式编码后的密码串。
 * 可以添加自定义的编解码，也可以修改默认的编解码器，只需修改默认的encodingId。
 * 优点：如果有一天我们对密码编码规则进行替换或者轮转。现有的用户不会受到影响。只要修改默认的DelegatingPasswordEncoder的idForEncode
 *
 * @author Jasmine
 * @version 1.0
 * @date 2020-11-09 16:44:44
 * @see PasswordEncoderConfiguration
 * @since JDK1.8
 */
@AutoConfiguration
@EnableConfigurationProperties(PasswordEncoderProperties.class)
@ConditionalOnProperty(prefix = PropertiesConstants.PASSWORD_ENCODER, name = PropertiesConstants.ENABLED,
    havingValue = "true", matchIfMissing = true)
public class PasswordEncoderConfiguration {
    private final PasswordEncoderProperties properties;

    public PasswordEncoderConfiguration(PasswordEncoderProperties properties) {
        this.properties = properties;
    }

    /**
     * 密码加密解密
     *
     * @see DelegatingPasswordEncoder
     * @see PasswordEncoderFactories
     */
    @Bean
    @SuppressWarnings("deprecation")
    public PasswordEncoder passwordEncoder(List<PasswordEncoder> passwordEncoderList) {
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        encoders.put("bcrypt", new BCryptPasswordEncoder());
        encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
        encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
        encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
        encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
        encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());
        encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
        encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
        encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
        encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
        encoders.put("SHA-256",
            new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
        encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
        encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
        encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());

        // 添加自定义的密码编解码器
        ExecuteUtils.executeNotEmpty(passwordEncoderList, () -> {
            passwordEncoderList.forEach(passwordEncoder -> encoders
                .put(passwordEncoder.getClass().getSimpleName().toLowerCase(), passwordEncoder));
        });
        String encodingId = properties.getEncodingId();
        ExceptionUtil.unTrue(encoders.keySet().contains(encodingId), "所填 [" + encodingId + "] 密码编码器不存在！");
        return new DelegatingPasswordEncoder(encodingId, encoders);
    }
}
